#include "hardwareinterface.hpp"

HardwareInterface::HardwareInterface(QObject *parent) :
    QObject(parent), cr('\x0D')
{
    this->mutex = new QMutex(QMutex::NonRecursive);
    QMutexLocker locker(this->mutex);

    this->dataMemoryOrganisation = NULL;

    this->settings = AppSettings::getInstance();

    this->serialPort = NULL;
    this->initialized = false;
    this->currentState = ENDREAD;
    this->readBuffer = "";
}


HardwareInterface::~HardwareInterface()
{
    this->settings = NULL;
    AppSettings::releaseInstance();
    delete this->mutex;
    delete this->serialPort;
}


void HardwareInterface::setDataMemoryOrganisation(DataMemoryOrganisation *dmo)
{
    this->dataMemoryOrganisation = dmo;
}


void HardwareInterface::processSerialPortQuery()
{
    QMutexLocker locker(this->mutex);

    if (!this->initialized || this->currentState != ENDREAD)
	return;

    // Get Registers to send
    int regToSend[4];
    regToSend[0] = this->dataMemoryOrganisation->getPortA()->getTrisValue();
    regToSend[1] = this->dataMemoryOrganisation->getPortA()->getValue(true);
    regToSend[2] = this->dataMemoryOrganisation->getPortB()->getTrisValue();
    regToSend[3] = this->dataMemoryOrganisation->getPortB()->getValue(true);

    // Build up the message data
    QString buffer;
    buffer.clear();
    for (int i = 0; i < 4; i++)
    {
	buffer.append((char) ((regToSend[i] & 0xF0) >> 4) | 0x30);
	buffer.append((char) (regToSend[i] & 0x0F) | 0x30);
    }
    buffer.append(this->cr); //Append Carriage Return

    this->serialPort->write(buffer.toAscii().constData());
    this->currentState = STARTREAD;
    this->serialPort->flush();
    emit dataWritten(buffer.trimmed());
}


void HardwareInterface::readFromSerialPort()
{
    QMutexLocker locker(this->mutex);

    if (!this->initialized)
	return;

    while (this->serialPort->bytesAvailable() > 0)
    {
	QByteArray read = this->serialPort->read(17);
	this->readBuffer.append(read);

	if (*this->readBuffer.right(1).toAscii().constData() == this->cr)
	    break;
    }

    // Split Input into the needed parts
    this->readPortA = (((int) this->readBuffer.at(0).toAscii()) & 0x0F) << 4;
    this->readPortA |= ((int) this->readBuffer.at(1).toAscii()) & 0x0F;
    this->readPortB = (((int) this->readBuffer.at(2).toAscii()) & 0x0F) << 4;
    this->readPortB |= ((int) this->readBuffer.at(3).toAscii()) & 0x0F;

    this->currentState = ENDREAD;
    locker.unlock();
    processInformation();
}


void HardwareInterface::processInformation()
{
    QMutexLocker locker(this->mutex);

    if (!this->initialized || this->currentState != ENDREAD)
	return;

    // Check for MCLR
    if (this->readPortA == 0 && this->readPortB == 0)
    {
	emit mclrReceived();

    // Write the data into the registers
    } else {
	this->dataMemoryOrganisation->getPortA()->setValue(this->readPortA, true);
	this->dataMemoryOrganisation->getPortB()->setValue(this->readPortB, true);
    }

    emit dataRead(this->readBuffer);

    this->readBuffer.clear();
    this->readPortA = 0;
    this->readPortB = 0;
    this->waitForAnswer = false;
}


void HardwareInterface::onManualReceive(QString data)
{
    QMutexLocker locker(this->mutex);

    this->readBuffer = data;

    // Split Input into the needed parts
    this->readPortA = (((int) this->readBuffer.at(0).toAscii()) & 0x0F) << 4;
    this->readPortA |= ((int) this->readBuffer.at(1).toAscii()) & 0x0F;
    this->readPortB = (((int) this->readBuffer.at(2).toAscii()) & 0x0F) << 4;
    this->readPortB |= ((int) this->readBuffer.at(3).toAscii()) & 0x0F;

    this->currentState = ENDREAD;
    locker.unlock();
    processInformation();
}


void HardwareInterface::onDsrChanged(bool status)
{
    if (status)
	qDebug() << "Device was turned on";
    else
	qDebug() << "Device was turned off";
}


void HardwareInterface::setSerialPort(QString name)
{
    QMutexLocker locker(this->mutex);

    this->readBuffer.clear();
    this->readPortA = 0;
    this->readPortB = 0;

    if (this->initialized)
    {
	this->serialPort->close();
	delete this->serialPort;
	this->initialized = false;
    }

    this->serialPort = new AbstractSerial(this);
    // Connect needed Signals

    // Initialize
    this->serialPort->setDeviceName(name);
    if (this->serialPort->open(QIODevice::ReadWrite | QIODevice::Unbuffered))
    {
	// Configure QextSerialPort
	this->serialPort->setBaudRate(AbstractSerial::BaudRate4800);
	this->serialPort->setDataBits(AbstractSerial::DataBits8);
	this->serialPort->setFlowControl(AbstractSerial::FlowControlOff);
	this->serialPort->setParity(AbstractSerial::ParityNone);
	this->serialPort->setStopBits(AbstractSerial::StopBits1);
	connect(this->serialPort, SIGNAL(readyRead()), this, SLOT(readFromSerialPort()), Qt::DirectConnection);

	this->initialized = true;
	emit stateChanged(true);
    } else {
	emit stateChanged(false);
	emit error(this->serialPort->deviceName(), this->serialPort->errorString());
    }
}


void HardwareInterface::stop()
{
    this->serialPort->close();
    delete this->serialPort;
    this->initialized = false;
    emit stateChanged(false);
}


void HardwareInterface::synchronousQuery()
{
    if (!this->initialized || !this->mutex->tryLock())
	return;

    this->mutex->unlock();
    this->processSerialPortQuery();
    if (!this->serialPort->waitForReadyRead(2000));
	this->currentState = ENDREAD;
}
